feat(tools): add SDK idempotency keys for safe memory write retries - #1628
feat(tools): add SDK idempotency keys for safe memory write retries#1628Sravanjangam wants to merge 1 commit into
Conversation
a84003f to
71be386
Compare
TracePull Review @yesprasad reviewed this PRNo concrete regression was found in this PR. The change adds optional SDK idempotency keys for memory and document writes while preserving existing inputs and contracts. DeepGraph traced the change across:
Recommended validation:
No blocking review comment is required based on the available evidence.
|
yesprasad
left a comment
There was a problem hiding this comment.
TracePull Review @yesprasad reviewed this PR
No concrete regression was found in this PR.
The change adds optional SDK idempotency keys for memory and document writes while preserving existing inputs and contracts.
DeepGraph traced the change across:
- 4 changed files
- 23 affected artifacts
- 4 direct consumers
- 19 transitive consumers
- Maximum depth: 4
- 49 workspace imports resolved
- 0 unresolved imports
Recommended validation:
- Confirm that
Idempotency-Keyis forwarded byclient.add()andclient.documents.add(). - Confirm that
crypto.subtleis available in all supported Node, Edge, and browser runtimes. - Document that metadata is excluded from the generated key, so identical content and tags within the same minute may produce the same key.
No blocking review comment is required based on the available evidence.
Disclaimer: DeepGraph is open source and provides this analysis free of charge. This review is informational only and should be independently validated by the maintainers.
Phase A — SDK-only, Fixes supermemoryai#1627. Generate Idempotency-Key = SHA256(normalizedContent|sorted tags|minuteBucket) and attach as Idempotency-Key header on addMemory / documentAdd via Supermemory client RequestOptions. Header is optional for the backend (Phase B will honor it). Prevents duplicate memories on network retries, enables safe retries and offline queue. Staff improvements: customIdempotencyKey (user-provided priority), RetryContext (reuses key across minute rollover), NFC+trim normalization, Why SHA-256 rationale, 14 Vitest tests. Co-authored-by: Sravanjangam <163002695+Sravanjangam@users.noreply.github.com>
71be386 to
9960935
Compare
|
Thanks @yesprasad for the DeepGraph review — addressed all three validation points in the latest push 1.
2.
3. Metadata excluded from key — ✅ intentional, now documented
Let me know if you'd like the metadata included in V1 — happy to extend Biome CI fix also landed ( |
Summary
Adds SDK-level idempotency keys for safe memory write retries — Phase A (SDK-only) of the Hierarchy. Generates a stable
Idempotency-Keyheader on everyaddMemory/documentAddcall so network retries and caller retries do not create duplicate memories. Updated with Staff improvements: custom key,RetryContext, NFC normalization, 14 Vitest tests.Fixes #1627
Problem
packages/tools/src/ai-sdk.ts:addMemoryToolanddocumentAddTool(andopenai/tools.ts) calledclient.add({ content, containerTags })with no idempotency signal. On retry (thesupermemoryclient retries 2× on 5XX/network, plus caller retries), the backend creates duplicate memories/embeddings/writes.packages/tools/src/ai-sdk.ts:102—client.addwith noIdempotency-Keypackages/tools/src/openai/tools.ts:290— sameImpact
content|tags|minuteBuckethash prevents the commonfetch failed → retry → duplicateloop on flaky networks/mobile.RetryContextcover both one-off and session-scoped retries.Solution — Phase A (SDK-only, mergeable now, no backend change)
New file
packages/tools/src/shared/idempotency.ts(96 lines, zero deps):Why SHA-256?
Core helpers:
generateIdempotencyKey(content, containerTags, now?, customKey?)— uses Web Cryptocrypto.subtle(Node 20+).customKeypriority: user-provided → generated (ifcustomKeyis non-empty, returned as-is).buildIdempotencyHeaders(content, containerTags, now?, customKey?) → { "Idempotency-Key": key }createRetryContext(content, containerTags, now?, customKey?) → { key, headers, getKey(), getHeaders() }— future-proof retry helper that captures the key once and reuses it across retries, even across minute rollovers:Wired in
packages/tools/src/ai-sdk.ts(addMemoryTool+documentAddToolnow accept optionalidempotencyKeyinput) andpackages/tools/src/openai/tools.ts:Header is optional for the backend — ignored until Phase B honors it, so this PR is useful even before server support. No breaking change, no new package. Tool
addMemorynow exposesidempotencyKey?: stringinput for developers who already have a key.Benchmark
Key generation is
SHA-256of a short string — ~0.05–0.1 ms per call (measured locally via Vitest,crypto.subtle); negligible vs. the network call it protects. No impact onsearchMemoriesor other tools.addMemoryheaderIdempotency-Key: <64-hex>Failure Handling
content.normalize("NFC").trim()before hashing — Unicode stable (caféNFC ==cafe\u0301NFD) and trailing whitespace stable ("hello "=="hello"), cross-platform stable.|tags|bucket).containerTagsorder independence — sorted before hashing so["b","a"]and["a","b"]produce the same key.RetryContextintentionally reuses the captured key across rollovers for safe retry.nowinjection for testing and for callers that want to control bucketing.Memory Footprint
Stateless — no cache, no storage. One
SHA-256per write (~64 bytes hex), plus optionalRetryContext(~80 bytes). No growth.Testing
Real Vitest —
bun x vitest run packages/tools/src/shared/idempotency.test.ts— 14 tests, 5 ms:nowinjection respectedIdempotency-KeyBiome:
bun x biome check packages/tools/src/shared/idempotency.ts— clean.Typecheck:
bun x tsc --noEmit --project packages/tools/tsconfig.json— no new errors (pre-existingai-sdk.tstool overload errors verified onmainviagit stash).Vitest: 14/14 pass (was 10/10, +4 Staff-requested edge cases).
Environment
supermemory@3.0.0-alpha.26fix/idempotent-memory-writes@a84003f1(force-pushed toSravanjangam/supermemory)supermemoryai/supermemory@main d436792eNon-goals (Phase A)
userIdin hash (backend can hashapiKeyserver-side if needed)Deferred roadmap
Idempotency-Key(dedupe store,409or200replay)customIdintegration